import java.io.File

import Main.Node
import org.apache.commons.lang3.StringUtils

import scala.collection.mutable
import scala.collection.mutable.ArrayBuffer
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global


/**
  * Created by yz on 2019/1/25
  */

case class RegionData(chr: String, start: Int, end: Int)

case class IdInfo(id: String, chr: String, pos: Int, index: Int)


object Tool {

  val parent = new File("G:\\rice90k_data_deal\\test")

  def filterNodes(nodes: mutable.Buffer[Node], groupNxRate: Double) = {
    nodes.filter { node =>
      val values = node.values
      val filterValues = values.filter(x => isNX(x))
      filterValues.size / values.size.toDouble <= groupNxRate
    }

  }

  def getClusterMap(nodes: mutable.Buffer[Node], sameRate: Double) = {
    val startTime = System.currentTimeMillis()

    val map = nodes.map { x =>
      (x.sample -> ArrayBuffer[Node](x))
    }.toMap
    val myMap = mutable.Map[String, ArrayBuffer[Node]]() ++= map

    val distanceMap = mutable.Map[(String, String), Boolean]()
    //    for {i <- 0 until nodes.size
    //         j <- (i + 1) until nodes.size
    //    } {
    //      val node1 = nodes(i)
    //      val node2 = nodes(j)
    //      val isNear = isNearNode(node1, node2, sameRate)
    //      distanceMap += ((node1.sample, node2.sample) -> isNear)
    //      distanceMap += ((node2.sample, node1.sample) -> isNear)
    //    }

    def loop: Unit = {
      val beforeSize = myMap.size
      myMap.foreach { case (key, nodes) =>
        if (myMap.isDefinedAt(key)) {
          val newMap = myMap - key
          newMap.foreach { case (otherKey, otherNodes) =>
            val outOpt = nodes.find { node =>
              otherNodes.find { otherNode =>
                //                                distanceMap((node.sample, otherNode.sample))
                val t = (node.sample, otherNode.sample)
                if (distanceMap.isDefinedAt(t)) {
                  distanceMap(t)
                } else {
                  val b = isNearNode(node, otherNode, sameRate)
                  distanceMap += ((node.sample, otherNode.sample) -> b)
                  distanceMap += ((otherNode.sample, node.sample) -> b)
                  b
                }

              }.isDefined
            }
            if (outOpt.isDefined) {
              myMap(key) ++= myMap(otherKey)
              myMap -= otherKey
            }
          }
        }

      }
      val size = myMap.size
      if (size < beforeSize) loop
    }


    loop
    myMap.toMap
  }

  def getNodes(sampleMap: Map[String, StringBuilder], needIdInfos: mutable.Buffer[IdInfo]) = {
    sampleMap.map { case (sample, genes) =>
      val values = needIdInfos.map { idInfo =>
        genes(idInfo.index).toString
      }
      Node(sample, values)
    }.toBuffer
  }

  def isNX(gene: String) = {
    gene == "X" || gene == "N"
  }

  def columnFilter(sampleMap: Map[String, mutable.StringBuilder], hybrid: Double) = {
    sampleMap.filter { case (key, snps) =>
      val nxSnps = snps.filter(x => Tool.isNX(x.toString))
      nxSnps.size / snps.size.toDouble < hybrid
    }
  }


  def isNearNode(node1: Node, node2: Node, sameRate: Double) = {
    val values1 = node1.values
    val values2 = node2.values
    var num = 0
    for (i <- 0 until values1.size) {
      val x = values1(i)
      val y = values2(i)
      if (x == y && x != "N" && y != "N" && x != "X" && y != "X") num += 1
    }
    val rate = num / values1.size.toDouble
    rate >= sameRate
  }

  def getRepresentMap(map: mutable.LinkedHashMap[String, ArrayBuffer[Node]]) = {
    map.map { case (key, nodes) =>
      val firstValues = nodes.head.values
      val trueValues = firstValues.map { x => ArrayBuffer[String]() }
      nodes.foreach { node =>
        val values = node.values
        values.zipWithIndex.foreach { case (v, i) =>
          trueValues(i) += v
        }
      }
      val representStr = trueValues.map { siteValues =>
        siteValues.groupBy(x => x).mapValues(x => x.size).toList.sortWith((x, y) => x._2 > y._2).
          filter(x => x._1 != "N" && x._1 != "X").headOption.map(_._1).getOrElse("N")
      }.mkString("-")
      (key, representStr)
    }

  }

  def getGroupMap(map: Map[String, ArrayBuffer[Node]]) = {
    val newMap = mutable.LinkedHashMap[String, ArrayBuffer[Node]]()
    val f4Key = map.find { case (key, nodes) =>
      nodes.map(_.sample).contains("F4")
    }

    val indexMap = if (f4Key.isDefined) {
      newMap += (s"1" -> f4Key.get._2)
      (map - f4Key.get._1)
    } else {
      map
    }
    indexMap.toList.zipWithIndex.foreach { case ((key, nodes), i) =>
      newMap += (s"${i + 2}" -> nodes)
    }
    newMap

  }

  def getGroupMapV2(map: Map[String, ArrayBuffer[Node]]) = {
    val newMap = mutable.LinkedHashMap[String, ArrayBuffer[Node]]()
    map.toList.zipWithIndex.foreach { case ((key, nodes), i) =>
      newMap += (s"${i + 1}" -> nodes)
    }
    newMap

  }

  def productRegionFile(file: File) = {
    val xlsxFile = new File(parent, "HHZ-cHTBs-List.xlsx")
    val lines = Utils.xlsx2Lines(xlsxFile, 0).map(_.split("\t").take(3).mkString("\t"))
    Utils.lines2File(new File(parent, "region.txt"), lines)
  }

  def productSnpFile = {
    val startTime = System.currentTimeMillis()
    val finalLines = ArrayBuffer[StringBuilder]()
    (10 to 10).foreach { i =>
      println(i)
      val file = new File(parent, s"5Kb_5K_raw_Gt.hmp.txt.add_90k_name.hmp.txt.${i}")
      val lines = Utils.txt2Lines(file)
      Utils.logTime(startTime)
      val columnLines = Utils.lines2columns(lines)
      Utils.logTime(startTime)
      if (finalLines.isEmpty) {
        val tmpLines = columnLines.map { tmpColumns =>
          val finalColumns = ArrayBuffer(tmpColumns(0), tmpColumns(2), tmpColumns(3)) ++= tmpColumns.drop(12)
          (new StringBuilder).append(finalColumns.mkString("\t"))
        }
        finalLines ++= tmpLines
      } else {
        val newLines = columnLines.map(columns => columns.drop(12).mkString("\t"))
        finalLines.zipWithIndex.foreach { case (x, i) =>
          if (i % 30000 == 0) {
            println(i)
            Utils.logTime(startTime)
          }
          finalLines(i).append(newLines(i))
        }
      }
      println(s"$i out!")
      Utils.logTime(startTime)
    }
    //      Utils.lines2File(new File(parent, "Kb_5K_raw_Gt.hmp.txt.add_90k_name.hmp.txt"), finalLines)
    Utils.lines2File(new File(parent, "Kb_5K_raw_Gt.hmp.txt.add_90k_name.hmp_10.txt"), finalLines.map(_.toString()))

    Utils.logTime(startTime)


  }

  def dealF4File = {
    val f4File = new File(parent, "geneInfo_h4s.txt")
    val file = new File(parent, "Kb_5K_raw_Gt.hmp.txt.add_90k_name.hmp_10.txt")
    val lines = Utils.txt2Lines(file)
    val f4Lines = Utils.txt2Lines(f4File)
    val f4Headers = f4Lines.head.split("\t").drop(1)
    val newLines = ArrayBuffer[String](s"rs#\t${f4Headers.mkString("\t")}")
    val f4Map = Utils.lines2columns(f4Lines).drop(1).map { columns =>
      (columns(0), columns.drop(1).mkString("\t"))
    }.toMap
    val nnStr = f4Headers.map(x => "NN").mkString("\t")
    newLines ++= Utils.lines2columns(lines).drop(1).map { columns =>
      val id = columns(0)
      val gene = f4Map.getOrElse(id, nnStr)
      val finalGene = if (gene == "NA") "NN" else gene
      s"${id}\t${finalGene}"
    }
    Utils.lines2File(new File(parent, "h4s.txt"), newLines)
  }


}
